/******************************************************************************
  * @file    PT32X005_ifmc.c
  * @author  应用开发团队
  * @version V1.1.0
  * @date    2020/1/27
  * @brief    This file provides firmware functions to manage the following
  *          functionalities of the FLASH peripheral:
  *           + Initialization and Configuration
  *           + Flash Erase and Read\Write Function
  *           + Interrupts and flags management
  *
  ******************************************************************************
  * @attention
  *
  *
  *****************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "PT32X005_ifmc.h"


/** @defgroup FLASH
  * @brief FLASH driver modules
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/



/**
  * @brief  Sets the code latency value.
  * @param  FLASH_Latency: specifies the FLASH Latency value.
  *             This parameter can be one of the following values:
  *             @arg IFMC_WAIT_0: FLASH Zero Latency cycle
  *             @arg IFMC_WAIT_1: FLASH One Latency cycle
  * @retval None
  */
void IFMC_SetLatency(u32 FLASH_Latency)
{
	u32 tmpreg = 0;
	/* Check the parameters */
	assert_param(IS_FLASH_WAIT_SEL(FLASH_Latency));
	/* Read the ACR register */
	tmpreg = IFMC->CR;
	/* Sets the Latency value */
	tmpreg &= (uint32_t) (~((uint32_t)IFMC_CR_WAIT));
	tmpreg |= FLASH_Latency;
	/* Write the ACR register */
	IFMC->CR = tmpreg;
}

/**
  * @brief  Sets the IFMC Prescaler.
  * @param  Prescaler: specifies the IFMC PSC value.
  *		    This parameter must be less than 64
  * @retval None
  */
void IFMC_SetPrescaler(u8 Prescaler)
{
	assert_param(IS_FLASH_PSC_Value(Prescaler));
	/* Write the PSC register */
	IFMC->PSC =Prescaler;
}

/**
  * @brief  Returns the FLASH Status.
  * @param  None
  * @retval FLASH Status: The returned value can be:
                 @arg   FLASH_BUSY
                 @arg   FLASH_KEY_ERR
                 @arg   FLASH_ADDR_ERR
                 @arg   FLASH_COMPLETE
  */
FLASH_Status IFMC_GetFlashStatus(void)
{
	FLASH_Status FLASHstatus = FLASH_COMPLETE;
	if((IFMC->SR & IFMC_SR_BUSY) == IFMC_SR_BUSY)
	{
		FLASHstatus = FLASH_BUSY;
	}
	else
	{
		if((IFMC->SR & (u32)(IFMC_SR_KERR)) != (u32)0x00)
		{
			FLASHstatus = FLASH_KEY_ERR;
		}
		else
		{
			if((IFMC->SR & (u32)(IFMC_SR_AERR)) != (u32)0x00)
			{
				FLASHstatus = FLASH_ADDR_ERR;
			}
			else
			{
				FLASHstatus = FLASH_COMPLETE;
			}
		}
	}
	/* Return the FLASH Status */
	return FLASHstatus;
}


/**
  * @brief  Waits for a FLASH operation to complete or a TIMEOUT to occur.
  * @param  Timeout: FLASH programming Timeout
  * @retval FLASH Status: The returned value can be:
                 @arg   FLASH_BUSY
                 @arg   FLASH_KEY_ERR
                 @arg   FLASH_ADDR_ERR
                 @arg   FLASH_COMPLETE
  */
FLASH_Status IFMC_WaitForLastFlashOperation(u32 Timeout)
{
	FLASH_Status status = FLASH_COMPLETE;
	/* Wait for a FLASH operation to complete or a TIMEOUT to occur */
	while((IFMC_GetFlashStatus() == FLASH_BUSY) && (Timeout != 0x00))
	{
		Timeout--;
	}
	if(Timeout == 0x00 )
	{
		status = FLASH_TIMEOUT;
	}
	/* Return the operation status */
	return status;
}


/**
  * @brief  Erases a specified page in program memory.
  * @param  Page_Address: The page address in program memory to be erased.
  * @retval FLASH Status: The returned value can be:
                 @arg   FLASH_BUSY
                 @arg   FLASH_KEY_ERR
                 @arg   FLASH_ADDR_ERR
                 @arg   FLASH_COMPLETE
  */
FLASH_Status IFMC_ErasePage(u32 Page_Address)
{
	u32 tmpreg = 0;
	FLASH_Status status = FLASH_COMPLETE;
	/* Check the parameters */
	assert_param(IS_FLASH_PROGRAM_ADDRESS(Page_Address));
	/* Wait for last operation to be completed */
	status = IFMC_WaitForLastFlashOperation(FLASH_ER_PRG_TIMEOUT);	//等待上一个操作完毕
	tmpreg = IFMC->CR;
	tmpreg &= ~ (0x000010F);
	if(status == FLASH_COMPLETE)
	{
		IFMC->AR  = Page_Address;
		if(((Page_Address) <  MAINCODE_END_ADDR))	//擦除地址小于Boot 程序存放地址范围(即主程序区域内的地址)
		{
			/* The main code area is erased by page */
			tmpreg |= IFMC_CR_KEY_MAIN | FLASH_OPMOD_WR |FLASH_OPCMD_PERA | FLASH_OPSTR_STRT;//删除| FLASH_OPCMD_PERA
			IFMC->CR = tmpreg;
			status = IFMC_WaitForLastFlashOperation(FLASH_ER_PRG_TIMEOUT);
		}
		else											//擦除地址大于Boot 程序存放地址范围(即用户信息区域内的地址)
		{
			status = FLASH_ADDR_ERR;
		}
	}
	/* Return the Erase Status */
	return status;
}

/**
  * @brief  Erases all FLASH pages.
  * @param  None
  * @retval FLASH Status: The returned value can be:
                 @arg   FLASH_BUSY
                 @arg   FLASH_KEY_ERR
                 @arg   FLASH_ADDR_ERR
                 @arg   FLASH_COMPLETE
  */
FLASH_Status IFMC_EraseAllPages(void)
{
	u32 tmpreg = 0;
	FLASH_Status status = FLASH_COMPLETE;
	tmpreg = IFMC->CR;
	tmpreg &= ~ (0x000010F);
	/* Wait for last operation to be completed */
	status = IFMC_WaitForLastFlashOperation(FLASH_ER_PRG_TIMEOUT);
	if(status == FLASH_COMPLETE)
	{
		/* if the previous operation is completed, proceed to erase all pages */
		tmpreg |= IFMC_CR_KEY_MAIN | FLASH_OPMOD_WR | FLASH_OPCMD_MERA  | FLASH_OPSTR_STRT;//删除| FLASH_OPCMD_MERA
		IFMC->CR = tmpreg;
		/* Wait for last operation to be completed */
		status = IFMC_WaitForLastFlashOperation(FLASH_ER_PRG_TIMEOUT);
	}
	/* Return the Erase Status */
	return status;
}


/**
* @brief  Programs a word at a specified address.
* @param  Address: specifies the address to be programmed.
* @param  Data: specifies the data to be programmed.
* @retval FLASH Status: The returned value can be:
              @arg   FLASH_BUSY
              @arg   FLASH_KEY_ERR
              @arg   FLASH_ADDR_ERR
              @arg   FLASH_COMPLETE
*/
FLASH_Status IFMC_ProgramWord(u32 Address, u32 Data)
{
	FLASH_Status status = FLASH_COMPLETE;
	__IO u32 tmp = 0;
	u32 tmpreg = 0;
	tmpreg = IFMC->CR;
	tmpreg &= ~ (0x000010F);
	/* Check the parameters */
	assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));
	/* Wait for last operation to be completed */
	status = IFMC_WaitForLastFlashOperation(FLASH_ER_PRG_TIMEOUT);
	if(status == FLASH_COMPLETE)
	{
		/* if the previous operation is completed, proceed to erase all pages */
		IFMC->AR  = Address;
		IFMC->DR  = Data;
		if(((Address) < MAINCODE_END_ADDR) && ((Address & 0x03) == 0))
		{
			/* Data is programming into the main code area */
			tmpreg |= IFMC_CR_KEY_MAIN | FLASH_OPMOD_WR | FLASH_OPCMD_PRO | FLASH_OPSTR_STRT;
			IFMC->CR = tmpreg;
			status = IFMC_WaitForLastFlashOperation(FLASH_ER_PRG_TIMEOUT);
		}
		else
		{
			status = FLASH_ADDR_ERR;
		}
	}
	/* Return the Program Status */
	return status;
}


/**
* @brief  Read a word at a specified address.
* @param  Address: specifies the address to be programmed.
* @retval  data at the specified address
*/
u32 IFMC_ReadWord(u32 Address)
{
	return *(__IO u32*) Address;
}

/**
* @brief  Read a half word at a specified address.
* @param  Address: specifies the address to be programmed.
* @retval   data at the specified address
*/
u16 IFMC_ReadHalfWord(u32 Address)
{
	return *(__IO u16*) Address;
}

/**
* @brief  Read a Byte at a specified address.
* @param  Address: specifies the address to be programmed.
* @retval   data at the specified address
*/
u8 IFMC_ReadByte(u32 Address)
{
	return *(__IO u8*) Address;
}

/**
  * @brief  Enables or disables the specified FLASH interrupts.
  * @param  FLASH_IT: specifies the FLASH interrupt sources to be enabled or disabled.
  *             This parameter can be any combination of the following values:
  *             @arg IFMC_IE_WOVI
  *             @arg IFMC_IE_POVI
  *             @arg IFMC_IE_COVI
  *             @arg IFMC_IE_CERI
  *             @arg IFMC_IE_KERI
  *             @arg IFMC_IE_AERI
  * @retval None
  */
void IFMC_ITConfig(u32 FLASH_IT, FunctionalState NewState)
{
	/* Check the parameters */
	assert_param(IS_FLASH_IT(FLASH_IT));
	assert_param(IS_FUNCTIONAL_STATE(NewState));
	if(NewState != DISABLE)
	{
		/* Enable the interrupt sources */
		IFMC->IE |= FLASH_IT;
	}
	else
	{
		/* Disable the interrupt sources */
		IFMC->IE &= ~(u32)FLASH_IT;
	}
}

/**
  * @brief  Checks whether the specified FLASH flag is set or not.
  * @param  FLASH_FLAG: specifies the FLASH flag to check.
  *             This parameter can be one of the following values:
  *             @arg IFMC_SR_WOV
  *             @arg IFMC_SR_POV
  *             @arg IFMC_SR_COV
  *             @arg IFMC_SR_BUSY
  *             @arg IFMC_SR_CERR
  *             @arg IFMC_SR_KERR
  *             @arg IFMC_SR_AERR
  * @retval The new state of FLASH_FLAG (SET or RESET).
  */
FlagStatus IFMC_GetFlagStatus(u32 FLASH_FLAG)
{
	FlagStatus bitstatus = RESET;
	/* Check the parameters */
	assert_param(IS_FLASH_SR_GET(FLASH_FLAG));
	if((IFMC->SR & FLASH_FLAG) != (u32)RESET)
	{
		bitstatus = SET;
	}
	else
	{
		bitstatus = RESET;
	}
	/* Return the new state of FLASH_FLAG (SET or RESET) */
	return bitstatus;
}

/**
  * @brief  Clears the FLASH's pending flags.
  * @param  FLASH_FLAG: specifies the FLASH flags to clear.
  *             This parameter can be any combination of the following values:
  *             @arg IFMC_SR_WOV
  *             @arg IFMC_SR_POV
  *             @arg IFMC_SR_COV
  *             @arg IFMC_SR_BUSY
  *             @arg IFMC_SR_CERR
  *             @arg IFMC_SR_KERR
  *             @arg IFMC_SR_AERR
  * @retval None
  */
void IFMC_ClearFlag(u32 FLASH_FLAG)
{
	/* Check the parameters */
	assert_param(IS_FLASH_SR_SEL(FLASH_FLAG));
	/* Clear the flags */
	IFMC->SR |= FLASH_FLAG;
}


/**
  * @brief  Returns the Boot Status.
  * @param  None
  * @retval Boot Status.
  */
RemapStatus IFMC_GetBootStatus(void)
{
	u32 tmp;
	tmp = (IFMC->BSR & IFMC_BSR_BSR);
	if (tmp != RESET)
	{
		return SET;
	}
	else
	{
		return RESET;
	}
}

/**
  * @brief  Returns the ReadProtect Status.
  * @param  None
  * @retval ReadProtect Status
  */
ProtectStatus IFMC_GetReadProtectStatus(void)
{
	u32 tmp;
	tmp = (IFMC->RPT & IFMC_RPT_PSR);
	if (tmp != RESET)
	{
		return SET;
	}
	else
	{
		return RESET;
	}
}


/**
  * @}
  */
